fix: stability improvements batch 4 (Tier 2 cleanup)#2861
Merged
simonredfern merged 4 commits intoJul 13, 2026
Merged
Conversation
SimpleDateFormat's parse and format methods both mutate internal state; shared singletons silently return wrong dates under concurrency. Replace mutable shared instances with ThreadLocal-backed accessors on the hot paths (APIUtil, Helper), and per-call defs for low-frequency sites (ConsentScheduler, OBPDataImport). Dead code removal: delete DateFormatWithCurrentTimeZone, which mutated the shared format's timezone and is not called anywhere. Add unit test hammering parseObpStandardDate and format/re-parse round-trip from 32 concurrent threads to ensure all results are identical and correct.
ExecutionContext.global is CPU-sized, and blocking I/O (DataSource connection acquisition via Doobie's connectEC, gRPC dispatch) on that pool starves all request Futures waiting on the same small thread count. Introduce BlockingIoExecutionContext, a fixed-size thread pool (default 50, prop-overridable) dedicated to blocking JDBC and gRPC work. Replace global at 6 sites: - DoobieTransactor.scala (2 sites: fallback + update transactors) - StoredProcedureUtils.scala (SP connector transactor) - ProjectionDb.scala (projection transactor) - ObpGrpcServer.scala (gRPC server dispatch) - Boot.scala:1074 (production gRPC server creation) Default pool size covers Hikari maximumPoolSize sum (main 20 + SP 20) with headroom; override via blocking_io_pool.size prop if Hikari pools resize.
… sent The password-reset endpoint (POST /management/user/reset-password-url) called sendHtmlEmail and discarded the result, returning 'sent' unconditionally even when the SMTP send failed. The caller (admin with canCreateResetPasswordUrl) already knows the target user's email, so anti-enumeration does not apply here. Switch to sendHtmlEmailEither and surface SMTP send failures as 500 responses instead of lying that the email was sent. Restructure the for-comprehension to hoist token generation before the send, and fail immediately on send failure. Add test scenario: verify that when SMTP is misconfigured (closed port) and test mode is off, the endpoint returns 500 with a message indicating the send failed, not the successful 'sent' response.
Since the json4s migration, JValue itself extends scala.Product, so a raw json.parse(...) result satisfies ResourceDoc example-body parameter types and compiles. However, resource-docs serialization then reflects on it as a generic case class instead of taking the JvalueCaseClass special-case unwrap path, causing schema mismatches and potential asInstanceOf[JvalueCaseClass] failures. All 20 affected UK OB v3.1 files share an identical private parseBody helper used only for ResourceDoc example bodies (79 call sites total, never in handler logic). Wrap the helper return in JvalueCaseClass(...) so serialization takes the correct path. This matches the earlier Berlin Group v1.3 fix (30afe2a) that resolved the same root cause.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Four independent Tier 2 stability fixes, one commit each:
SimpleDateFormatinstances inAPIUtil/Helperwere not thread-safe (bothparseandformatmutate internal state); switched hot paths toThreadLocal-backed accessors and low-frequency sites to per-call instances. Deleted unusedDateFormatWithCurrentTimeZone.BlockingIoExecutionContext, a dedicated bounded thread pool for blocking JDBC connection acquisition and gRPC dispatch, replacingExecutionContext.globalat 6 call sites so blocked I/O cannot starve request-handling Futures on the CPU-sized global pool.Box[String]result of sending the reset email and always returned"sent", even when SMTP failed. Now surfaces SMTP failures as a 500 response; token generation still happens before the send attempt so a retry does not lose progress.parseBodyhelper that returned a rawJObject; wrapped inJvalueCaseClassso swagger/resource-docs serialization takes the correct unwrap path (same bug class as the earlier Berlin Group v1.3 fix).Test plan
mvn install -pl .,obp-commons -am -DskipTests— BUILD SUCCESS (JDK 25)mvn compile -pl obp-api -am— BUILD SUCCESSDateFormatConcurrencyTest(32 concurrent threads) — passedPasswordResetTestfull suite, including new SMTP-failure scenario — 17/17 passedgit grep -n 'new SimpleDateFormat' obp-api/src/main— no remaining shared singleton valsgit grep -n 'ExecutionContext.global' obp-api/src/main— the 6 target sites are gone